<script setup lang="ts">
import { computed } from 'vue'
import { NIcon, NTooltip } from 'naive-ui'

interface MenuItem {
  type: string
  label: string
  key: string
  icon: any
  activeClass?: string
  hoverClass?: string
  normalClass?: string
  disabled?: boolean
}

const props = defineProps({
  modelValue: {
    type: String,
    required: true
  },
  menuItems: {
    type: Array as PropType<MenuItem[]>,
    required: true,
    validator: (value: MenuItem[]) => {
      return value.every(
        (item) =>
          typeof item === 'object' &&
          'key' in item &&
          'label' in item &&
          'icon' in item &&
          ('type' in item || 'disabled' in item) &&
          (item.activeClass === undefined || typeof item.activeClass === 'string') &&
          (item.hoverClass === undefined || typeof item.hoverClass === 'string') &&
          (item.normalClass === undefined || typeof item.normalClass === 'string')
      )
    }
  },
  isCollapsed: {
    type: Boolean,
    default: false
  },
  activeClass: {
    type: String
  },
  hoverClass: {
    type: String
  }
})

const emit = defineEmits(['update:modelValue', 'itemClick', 'update:isCollapsed'])

const sidebarClass = computed(() => ({
  sidebar: true,
  'sidebar--collapsed': props.isCollapsed,
  'bg-gray-100': true
}))

const menuItemClass = (item: MenuItem) => {
  const isActive = item.key === props.modelValue
  return [
    'menu-item',
    props.isCollapsed && 'menu-item--collapsed',
    isActive ? (item.activeClass || props.activeClass || 'menu-item--active') : (item.normalClass || ''),
    item.hoverClass || props.hoverClass || 'menu-item--hover',
    item.disabled && 'menu-item--disabled'
  ]
}

const handleItemClick = (item) => {
  if (item.disabled) return
  emit('update:modelValue', item.key)
  emit('itemClick', item)
}

const groupedMenuItems = computed(() => {
  const groups = props.menuItems.reduce((acc, item) => {
    if (item.type === 'group') {
      acc.push({
        type: 'group',
        label: item.label,
        items: []
      })
    } else {
      if (acc.length === 0 || acc[acc.length - 1].type !== 'group') {
        acc.push({ type: 'menu', items: [] })
      }
      acc[acc.length - 1].items.push(item)
    }
    return acc
  }, [])
  return groups
})
</script>

<template>
  <aside :class="sidebarClass">
    <nav class="sidebar__nav">
      <ul>
        <slot name="before-menu"></slot>
        <slot>
          <template v-for="group in groupedMenuItems" :key="group.label || 'menu'">
            <li v-if="group.type === 'group'" class="menu-group">
              <span class="mobile:px-0 text-lg text-gray-500 break-all font-medium uppercase">
                {{ group.label }}
              </span>
            </li>
            <li
              v-for="item in group.items"
              :key="item.key"
              :class="menuItemClass(item)"
              @click="handleItemClick(item)"
              :aria-disabled="item.disabled"
            >
              <NIcon size="20">
                <component :is="item.icon" />
              </NIcon>
              <NTooltip v-if="isCollapsed" placement="right" trigger="hover">
                <template #trigger>
                  <span></span>
                </template>
                {{ item.label }}
              </NTooltip>
              <span v-if="!isCollapsed" class="menu-item__label">{{ item.label }}</span>
            </li>
          </template>
        </slot>
        <slot name="after-menu"></slot>
      </ul>
    </nav>
  </aside>
</template>

<style scoped lang="scss">
$sidebar-width: 200px;
$sidebar-collapsed-width: 64px;
$transition-duration: 0.3s;
$active-color: #155aef;
$hover-bg-color: #155aef14;

.sidebar {
  width: $sidebar-width;
  transition: width $transition-duration;

  &--collapsed {
    width: $sidebar-collapsed-width;
    display: flex;
    justify-content: center;
  }

  &__nav ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
  }
}

.menu-item {
  display: flex;
  align-items: center;
  padding: 8px 10px;
  margin-bottom: 5px;
  cursor: pointer;
  border-radius: 8px;
  transition: background-color 0.3s;

  &--collapsed {
    justify-content: center;
    width: 50px;
  }

  &--active {
    background-color: v-bind('props.activeClass || $hover-bg-color');

    .n-icon,
    .menu-item__label {
      color: $active-color;
      cursor: pointer;
      font-weight: 600;
    }
  }

  &--hover:hover {
    background-color: v-bind('props.hoverClass || $hover-bg-color');

    .n-icon,
    .menu-item__label {
      color: $active-color;
      cursor: pointer;
      font-weight: 600;
    }
  }

  &--disabled {
    opacity: 0.5;
    cursor: not-allowed;

    .n-icon,
    .menu-item__label {
      color: #999;
    }

    &:hover {
      background-color: transparent;
    }
  }

  &__label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-left: 10px;
    font-weight: bolder;
  }
}

.menu-group {
  padding: 5px;
  font-weight: bold;
  color: $active-color;

  &__label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

.n-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
